package com.xlongwei.accounts.service;

import java.time.LocalDate;
import java.time.temporal.TemporalAdjusters;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.noear.solon.Utils;
import org.noear.solon.annotation.Component;
import org.noear.solon.annotation.Inject;
import org.smartboot.http.common.utils.NumberUtils;

import com.xlongwei.accounts.config.MySqlUtils;
import com.xlongwei.accounts.controller.RecordController;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Component
public class AccountService {
    @Inject
    MySqlUtils sqlUtils;

    public Map<Integer, Long> balances(Integer userid) {
        return balances(userid, null);
    }

    public Map<Integer, Long> balances(Integer userid, LocalDate end) {
        Map<Integer, Long> balances = new HashMap<>();
        // if(end == null) end = LocalDate.now();
        List<Long[]> list = end != null ? sqlUtils.sql(
                "select accountid_in accountid,sum(amount) balance from t_record where userid=? and `day`<=? and accountid_in>0 group by accountid_in",
                userid, end).queryRowList(rs -> {
                    return new Long[] {rs.getObject(1, Long.class), rs.getObject(2, Long.class)};
                })
                : sqlUtils.sql(
                "select accountid_in accountid,sum(amount) balance from t_record where userid=? and accountid_in>0 group by accountid_in",
                userid).queryRowList(rs -> {
                    return new Long[] {rs.getObject(1, Long.class), rs.getObject(2, Long.class)};
                });
        if (list != null) {
            balances.putAll(list.stream()
                    .collect(Collectors.toMap(arr -> arr[0].intValue(), arr -> arr[1])));
        }
        list = end != null ? sqlUtils.sql(
                "select accountid_out accountid,sum(amount) balance from t_record where userid=? and `day`<=? and accountid_out>0 group by accountid_out",
                userid, end).queryRowList(rs -> {
                    return new Long[] {rs.getObject(1, Long.class), rs.getObject(2, Long.class)};
                })
                : sqlUtils.sql(
                "select accountid_out accountid,sum(amount) balance from t_record where userid=? and accountid_out>0 group by accountid_out",
                userid).queryRowList(rs -> {
                    return new Long[] {rs.getObject(1, Long.class), rs.getObject(2, Long.class)};
                });
        if (list != null) {
            list.stream().collect(Collectors.toMap(arr -> arr[0].intValue(), arr -> arr[1]))
                    .entrySet().forEach(entry -> {
                        // 余额=入账-出账
                        balances.put(entry.getKey().intValue(),
                                balances.getOrDefault(entry.getKey(), 0L) - entry.getValue());
                    });
        }
        log.debug("userid={} balances={}", userid, balances);
        return balances;
    }

    public String creditbill(Integer accountid, int[] billRepayDay, LocalDate day) {
        LocalDate[] creditcycle = creditcycle(billRepayDay[0], day);
        LocalDate start = creditcycle[0], end = creditcycle[1];
        LocalDate repayDay = billRepayDay[1] <= 0 ? null : billRepayDay[1] > start.getDayOfMonth() ? (billRepayDay[1] > start.with(TemporalAdjusters.lastDayOfMonth()).getDayOfMonth() ? end.withDayOfMonth(1) : start.withDayOfMonth(billRepayDay[1])) : (billRepayDay[1] < end.getDayOfMonth() ? end.withDayOfMonth(billRepayDay[1]) : null);
        Object value = null; // 招卡21/9，账单=（上月账单日至当月账单日之间的支出）-（上月还款日至当月账单日之间的还款：即提前部分还款）
        if (repayDay != null) {
            value = sqlUtils
                .sql("select (select COALESCE(sum(amount),0) from t_record where accountid_out=? and `day` > ? and `day` <= ?)" + 
                " - (select COALESCE(sum(amount),0) from t_record where accountid_in=? and `day` > ? and `day` <= ?)",
                        accountid, start, end, accountid, repayDay, end)
                .queryValue();
        } else { // 招卡21，账单=上月账单日至当月账单日之间的支出，不方便计算提前还款的部分
            value = sqlUtils
                    .sql("select sum(amount) from t_record where accountid_out=? and `day` > ? and `day` <= ?",
                            accountid, start, end)
                    .queryValue();
        }
        return value == null ? null : RecordController.yuan(Integer.parseInt(value.toString()));
    }

    public String creditbill(Integer accountid, int dayOfMonth, LocalDate day) {
        LocalDate[] creditcycle = creditcycle(dayOfMonth, day);
        LocalDate start = creditcycle[0], end = creditcycle[1];
        Object value = sqlUtils
                .sql("select sum(amount) from t_record where accountid_out=? and `day` > ? and `day` <= ?",
                        accountid, start, end)
                .queryValue();
        return value == null ? null : RecordController.yuan(Integer.parseInt(value.toString()));
    }

    public static LocalDate[] creditcycle(int dayOfMonth, LocalDate today) {
        LocalDate start = null, end = today;
        if (end.getDayOfMonth() > dayOfMonth) {
            end = end.withDayOfMonth(dayOfMonth);
        } else {
            end = end.plusMonths(-1).with(TemporalAdjusters.lastDayOfMonth());
            if (end.getDayOfMonth() > dayOfMonth) {
                end = end.withDayOfMonth(dayOfMonth);
            }
        }
        start = end.plusMonths(-1).with(TemporalAdjusters.lastDayOfMonth());
        if (start.getDayOfMonth() > dayOfMonth) {
            start = start.withDayOfMonth(dayOfMonth);
        }
        log.debug("dayOfMonth={} today={} cycle=({},{}]", dayOfMonth, today, start, end);
        return new LocalDate[] { start, end };
    }

    public static int[] billRepayDay(String name) {
        int bill = 0, repay = 0;
        if (!Utils.isBlank(name)) {
            Pattern pattern = Pattern.compile("(\\d+)(/(\\d+))?");
            Matcher matcher = pattern.matcher(name);
            if (matcher.find()) {
                bill = NumberUtils.toInt(matcher.group(1), 0);
                repay = NumberUtils.toInt(matcher.group(3), 0);
            }
        }
        return new int[] { bill, repay };
    }
}
